package app.server.project.core.syndb;

import app.server.project.core.life.AppLifeMgr;
import app.server.project.core.life.IDeadLoopTask;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;

@Component
@Slf4j
@RequiredArgsConstructor
public class SynDBMgr implements IDeadLoopTask {


    private static final int LAZY_RATE = 10 * 1000;
    private static final int QUICK_RATE = 50;
    private static HashMap<String, ColumnCache> lazyMap = new HashMap<>();
    private static HashMap<String, ColumnCache> quickMap = new HashMap<>();

    private final JdbcTemplate jdbcTemplate;
    /**
     * 定时同步标记,防止关机的时候，发生争夺
     */
    private AtomicBoolean canSyn = new AtomicBoolean(false);

    /**
     * 加入变化数据到缓存
     *
     * @param
     */
    public static <T, R> void addChangeData(Getter<T, R> consumer, Object model) {
        try {
            var speed = model.getClass().getAnnotation(SynSpeed.class);
            var columnCacheHashMap = lazyMap;
            if (Objects.nonNull(speed)) {
                columnCacheHashMap = quickMap;
            }
            //参数
            //获取方法类命
            Object targetObject = model;

            //没有同步标志
            //获取主键值
            Object idVal = null;
            for (var field : targetObject.getClass().getDeclaredFields()) {
                var annId = field.getAnnotation(Id.class);
                if (Objects.nonNull(annId)) {
                    field.setAccessible(true);
                    idVal = field.get(targetObject);
                    break;
                }
            }
            //获取表名
            var tbName = targetObject.getClass().getAnnotation(Entity.class);
            if (Objects.isNull(tbName)) return;
            //属性
            String propertyName = FieldNameExtractor.extractFieldNameFromGetter(consumer);
            var field = targetObject.getClass().getDeclaredField(propertyName);
            var colAnn = field.getAnnotation(Column.class);
            String key = String.format("%s-%s", tbName.name(), colAnn.name());
            var colCache = columnCacheHashMap.get(key);
            field.setAccessible(true);
            colCache.add(idVal, field.get(targetObject));
        } catch (Exception e) {
            log.error("-------------", e);
        }
    }

    /**
     * 加入关机退出任务队列
     */
    @EventListener(ApplicationReadyEvent.class)
    public void addToAppExitList() {
        AppLifeMgr.LoopTasks.add(this);
    }

    /**
     * 加载数据库表结构
     *
     * @param tbModels
     */
    public void init(Set<Class<?>> tbModels) {
        tbModels.forEach(model -> {
            var fields = model.getDeclaredFields();
            //表名
            Entity annTb = (Entity) model.getAnnotation(Entity.class);
            if (Objects.isNull(annTb)) return;
            //主键名称
            Column annIdName = null;
            for (var field : fields) {
                var annId = field.getAnnotation(Id.class);
                if (Objects.nonNull(annId)) {
                    annIdName = field.getAnnotation(Column.class);
                    break;
                }
            }
            //列名
            for (var field : fields) {
                if (Objects.nonNull(field.getAnnotation(Id.class))) continue;
                var annField = field.getAnnotation(Column.class);
                if (Objects.isNull(annField)) continue;
                if (Objects.nonNull(annIdName)) {
                    var colCache = new ColumnCache(annTb.name(), annField.name(), this.jdbcTemplate, annIdName.name());
                    SynSpeed speed = model.getAnnotation(SynSpeed.class);
                    if (Objects.nonNull(speed)) {
                        quickMap.put(colCache.getKey(), colCache);
                    } else {
                        lazyMap.put(colCache.getKey(), colCache);
                    }

                }
            }
        });
        log.info("==========成功初始化延迟性表缓存池数量:{}", lazyMap.size());
        log.info("==========成功初始化无延迟性表缓存池数量:{}", quickMap.size());
    }


    /**
     * 1秒定时同步数据库
     */
    @Scheduled(fixedDelay = LAZY_RATE)
    public void lazySyn() {
        //尝试锁住
        var flag = this.canSyn.compareAndSet(false, true);
        if (!flag) return;
        try {
            lazyMap.forEach((k, v) -> {
                v.synDb();
            });
        } catch (Exception e) {
            log.error("同步数据库报错", e);
        }
        //解锁
        this.canSyn.compareAndSet(true, false);
    }

    @Scheduled(fixedDelay = QUICK_RATE)
    public void quickSyn() {
        //尝试锁住
        var flag = this.canSyn.compareAndSet(false, true);
        if (!flag) return;
        try {
            quickMap.forEach((k, v) -> {
                v.synDb();
            });
        } catch (Exception e) {

        }
        //解锁
        this.canSyn.compareAndSet(true, false);
    }

    @Override
    public void doDeadLoopBeforeExitApp() {
        var flag = this.canSyn.compareAndSet(false, true);
        if (!flag) return;
        lazyMap.forEach((k, v) -> {
            v.synDb();
        });
        quickMap.forEach((k, v) -> {
            v.synDb();
        });
    }
}
